import hashlib
import json
from collections import defaultdict
import config
import logging
import time
import os

LOG = logging.getLogger()
LOG.setLevel(logging.INFO)
_log_path = os.path.dirname(__file__) + '/log/'
_log_fname = os.path.join(_log_path, "app.log")
_formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
# _fh = logging.handlers.TimedRotatingFileHandler(filename=_log_fname, when="M", interval=1, backupCount=7, encoding="utf-8")
_fh = logging.FileHandler(_log_fname, encoding="utf-8")
_fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关
_fh.setFormatter(_formatter)
LOG.addHandler(_fh)
_console = logging.StreamHandler()
_console.setLevel(logging.DEBUG)
_console.setFormatter(_formatter)
LOG.addHandler(_console)

class NonceCache:
    def __init__(self, expire_time: int):
        self._expire_time = expire_time
        self._last_clean_time = 0
        self._cache = defaultdict(set)

    def is_key_exists(self, uid, nonce):
        _nonce = "%s:%s" % (uid, nonce)
        for _time, _set in self._cache.items():
            if _nonce in _set:
                return True
        return False

    def _get_nonce_key(self):
        now = int(time.time())
        cur_key = now // self._expire_time * self._expire_time
        return cur_key

    def record_nonce(self, uid, nonce):
        _nonce = "%s:%s" % (uid, nonce)
        self._clean_if_needed()
        if self.is_key_exists(uid, nonce):
            raise ValueError("已经存在. _nonce=%s" % str(_nonce))
        key = self._get_nonce_key()
        self._cache[key].add(_nonce)

    def _clean_if_needed(self):
        now = int(time.time())
        if self._last_clean_time + self._expire_time*2 >= now:
            return
        self._last_clean_time = now
        for _time in list(self._cache.keys()):
            if _time + self._expire_time*2 > now:
                continue
            LOG.debug("clean nonce cache, key=%s" % _time)
            self._cache.pop(_time)


def calc_sha1(text: str) -> str:
    hash = hashlib.sha1()
    hash.update(text.encode('utf-8'))
    return hash.hexdigest()


def calc_sign(text, isadmin=False):
    if isadmin:
        return calc_sha1(text + "#" + config.admin_token)
    else:
        return calc_sha1(text + "#" + config.client_token)


class ApiException(Exception):
    pass


class AuthException(ApiException):
    pass


class ApiRespBase:
    def __init__(self):
        self.code = None
        self.msg = None
        self.value = None

    @classmethod
    def create_error_resp(cls, code, msg):
        resp = ApiRespBase()
        resp.code = code
        resp.msg = msg
        return resp

    @classmethod
    def create_resp_ok(cls, value=None):
        resp = ApiRespBase()
        resp.code = 0
        resp.msg = ""
        resp.value = value if value else ""
        return resp

    def __str__(self):
        d = {
            "code": self.code,
            "msg": self.msg,
            "value": str(self.value) if self.value else ""
        }
        return json.dumps(d, ensure_ascii=False)


class EmptyResp(ApiRespBase):
    def __init__(self):
        super().__init__()
        self.value = ""


class BaseCmdResp(EmptyResp):
    pass


class CmdAiron(BaseCmdResp):
    def __init__(self, cmd):
        super().__init__()
        self.value = cmd

    @classmethod
    def create_cmd_airon_on(cls):
        return CmdAiron("on")

    @classmethod
    def create_cmd_airon_off(cls):
        return CmdAiron("off")
